package com.xhs.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;

/**
 * 卡密校验工具类
 * 
 * @author xhs
 */
public class LicenseValidator {
    
    private static final Logger logger = LoggerFactory.getLogger(LicenseValidator.class);
    
    /**
     * 密钥种子（必须与生成脚本中的密钥保持一致）
     */
    private static final String SECRET_KEY = "XHS-LICENSE-2024-SECURE-KEY-FOR-GENERATION";
    
    /**
     * 额外的校验密钥（增加破解难度）
     */
    private static final String VALIDATION_SALT = "XHS-SECURITY-VALIDATION-SALT-2024";
    
    /**
     * 获取当前机器的机器码
     * 
     * @return 机器码字符串
     */
    public static String getCurrentMachineCode() {
        try {
            StringBuilder hardwareInfo = new StringBuilder();
            
            // 获取CPU序列号
            String cpuId = getWindowsHardwareInfo("wmic cpu get ProcessorId /format:table");
            hardwareInfo.append(cpuId != null ? cpuId : "UNKNOWN_CPU");
            hardwareInfo.append("-");
            
            // 获取主板序列号
            String boardSerial = getWindowsHardwareInfo("wmic baseboard get SerialNumber /format:table");
            hardwareInfo.append(boardSerial != null ? boardSerial : "UNKNOWN_BOARD");
            hardwareInfo.append("-");
            
            // 获取系统UUID
            String systemUuid = getWindowsHardwareInfo("wmic csproduct get UUID /format:table");
            hardwareInfo.append(systemUuid != null ? systemUuid : "UNKNOWN_UUID");
            hardwareInfo.append("-");
            
            // 获取MAC地址
            String macAddress = getWindowsHardwareInfo("wmic path win32_networkadapter where \"PhysicalAdapter=true and NetConnectionStatus=2\" get MACAddress /format:table");
            if (macAddress == null) {
                // 备用方案
                macAddress = getWindowsHardwareInfo("wmic path win32_networkadapter where \"MACAddress is not null\" get MACAddress /format:table");
            }
            hardwareInfo.append(macAddress != null ? macAddress : "UNKNOWN_MAC");
            
            // 生成SHA256哈希
            String sha256Hash = generateSHA256(hardwareInfo.toString());
            
            // 格式化机器码
            return formatMachineCode(sha256Hash);
            
        } catch (Exception e) {
            logger.error("获取机器码失败", e);
            return null;
        }
    }
    
    /**
     * 执行Windows命令获取硬件信息
     * 
     * @param command 要执行的命令
     * @return 硬件信息值
     */
    private static String getWindowsHardwareInfo(String command) {
        try {
            Process process = Runtime.getRuntime().exec(command);
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK"));
            
            String line;
            boolean skipFirst = true;
            while ((line = reader.readLine()) != null) {
                line = line.trim();
                if (skipFirst) {
                    skipFirst = false;
                    continue; // 跳过表头
                }
                if (!line.isEmpty() && !line.contains("ProcessorId") && !line.contains("SerialNumber") 
                    && !line.contains("UUID") && !line.contains("MACAddress")) {
                    // 过滤掉无效值
                    if (!line.equalsIgnoreCase("To be filled by O.E.M.") 
                        && !line.equalsIgnoreCase("Not Available")
                        && !line.equalsIgnoreCase("Default string")) {
                        reader.close();
                        return line;
                    }
                }
            }
            reader.close();
            process.waitFor();
        } catch (Exception e) {
            logger.warn("执行命令失败: " + command, e);
        }
        return null;
    }
    
    /**
     * 生成环境指纹时使用的MD5哈希（保留用于环境指纹）
     * 
     * @param input 输入字符串
     * @return MD5哈希字符串
     */
    private static String generateMD5(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] hashBytes = md.digest(input.getBytes(StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            for (byte b : hashBytes) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        } catch (Exception e) {
            logger.error("生成MD5哈希失败", e);
            return "";
        }
    }
    
    /**
     * 格式化机器码（添加分隔符）
     * 
     * @param hash 原始哈希字符串
     * @return 格式化后的机器码
     */
    private static String formatMachineCode(String hash) {
        if (hash == null || hash.length() < 16) {
            return hash;
        }
        
        // 截取前32个字符（对于SHA256）
        String truncatedHash = hash.length() > 32 ? hash.substring(0, 32) : hash;
        
        StringBuilder formatted = new StringBuilder();
        for (int i = 0; i < truncatedHash.length(); i++) {
            if (i > 0 && i % 4 == 0) {
                formatted.append("-");
            }
            formatted.append(truncatedHash.charAt(i));
        }
        
        return formatted.toString();
    }
    
    /**
     * 根据机器码生成对应的卡密
     * 
     * @param machineCode 机器码
     * @return 生成的卡密
     */
    public static String generateLicenseKey(String machineCode) {
        if (machineCode == null || machineCode.trim().isEmpty()) {
            return null;
        }
        
        try {
            String combinedData = machineCode + ":" + SECRET_KEY;
            String sha256Hash = generateSHA256(combinedData);
            
            if (sha256Hash != null && sha256Hash.length() >= 20) {
                String licenseKey = sha256Hash.substring(0, 20);
                String formattedLicense = formatLicenseKey(licenseKey);
                return "LIC-" + formattedLicense;
            }
        } catch (Exception e) {
            logger.error("生成卡密失败", e);
        }
        
        return null;
    }
    
    /**
     * 生成SHA256哈希
     * 
     * @param input 输入字符串
     * @return SHA256哈希字符串
     */
    private static String generateSHA256(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            byte[] hashBytes = md.digest(input.getBytes(StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            for (byte b : hashBytes) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        } catch (Exception e) {
            logger.error("生成SHA256哈希失败", e);
            return null;
        }
    }
    
    /**
     * 格式化卡密（添加分隔符）
     * 
     * @param licenseKey 原始卡密
     * @return 格式化后的卡密
     */
    private static String formatLicenseKey(String licenseKey) {
        if (licenseKey == null || licenseKey.length() < 4) {
            return licenseKey;
        }
        
        StringBuilder formatted = new StringBuilder();
        for (int i = 0; i < licenseKey.length(); i++) {
            if (i > 0 && i % 4 == 0) {
                formatted.append("-");
            }
            formatted.append(licenseKey.charAt(i));
        }
        
        return formatted.toString();
    }
    
    /**
     * 验证卡密是否与当前机器码匹配（增强版）
     * 
     * @param inputLicenseKey 用户输入的卡密
     * @return true表示验证通过，false表示验证失败
     */
    public static boolean validateLicense(String inputLicenseKey) {
        if (inputLicenseKey == null || inputLicenseKey.trim().isEmpty()) {
            logger.warn("卡密为空");
            return false;
        }
        
        // 1. 基础卡密校验
        if (!performBasicLicenseValidation(inputLicenseKey)) {
            return false;
        }
        
        // 2. 环境完整性检查
        if (!performEnvironmentCheck()) {
            logger.warn("环境完整性检查失败");
            return false;
        }
        
        // 3. 时间戳校验（防止时间回滚攻击）
        if (!performTimestampValidation()) {
            logger.warn("时间戳校验失败");
            return false;
        }
        
        // 4. 额外的哈希校验
        if (!performAdvancedValidation(inputLicenseKey)) {
            logger.warn("高级校验失败");
            return false;
        }
        
        logger.info("所有校验通过");
        return true;
    }
    
    /**
     * 执行基础卡密校验
     * 
     * @param inputLicenseKey 输入的卡密
     * @return 校验结果
     */
    private static boolean performBasicLicenseValidation(String inputLicenseKey) {
        // 获取当前机器码
        String currentMachineCode = getCurrentMachineCode();
        if (currentMachineCode == null) {
            logger.error("无法获取当前机器码");
            return false;
        }
        
        // 生成预期的卡密
        String expectedLicenseKey = generateLicenseKey(currentMachineCode);
        if (expectedLicenseKey == null) {
            logger.error("无法生成预期卡密");
            return false;
        }
        
        // 比较卡密（忽略大小写）
        boolean isValid = inputLicenseKey.trim().equalsIgnoreCase(expectedLicenseKey.trim());
        
        if (isValid) {
            logger.info("基础卡密校验通过 - 机器码: {}", currentMachineCode);
        } else {
            logger.warn("基础卡密校验失败 - 输入卡密: {}, 预期卡密: {}, 机器码: {}", 
                inputLicenseKey, expectedLicenseKey, currentMachineCode);
        }
        
        return isValid;
    }
    
    /**
     * 执行环境完整性检查
     * 
     * @return 检查结果
     */
    private static boolean performEnvironmentCheck() {
        try {
            // 检查是否在调试环境中
            String debugProperty = System.getProperty("java.debug");
            if ("true".equals(debugProperty)) {
                logger.warn("检测到调试环境");
                return false;
            }
            
            // 检查JVM参数
            String javaCommand = System.getProperty("sun.java.command");
            if (javaCommand != null && javaCommand.contains("-agentlib")) {
                logger.warn("检测到调试代理");
                return false;
            }
            
            // 检查运行时环境
            Runtime runtime = Runtime.getRuntime();
            if (runtime.totalMemory() < 50 * 1024 * 1024) { // 小于50MB可能是受限环境
                logger.warn("运行环境可疑");
                return false;
            }
            
            return true;
        } catch (Exception e) {
            logger.warn("环境检查异常", e);
            return false;
        }
    }
    
    /**
     * 执行时间戳校验
     * 
     * @return 校验结果
     */
    private static boolean performTimestampValidation() {
        try {
            // 检查系统时间是否合理（不能太旧或太新）
            LocalDate now = LocalDate.now();
            LocalDate minDate = LocalDate.of(2024, 1, 1);
            LocalDate maxDate = LocalDate.of(2030, 12, 31);
            
            if (now.isBefore(minDate) || now.isAfter(maxDate)) {
                logger.warn("系统时间异常: {}", now);
                return false;
            }
            
            // 检查启动时间（防止快速重启攻击）
            long uptime = System.currentTimeMillis() - getJVMStartTime();
            if (uptime < 1000) { // 启动时间少于1秒可能是异常
                logger.warn("JVM启动时间异常: {}ms", uptime);
                return false;
            }
            
            return true;
        } catch (Exception e) {
            logger.warn("时间戳校验异常", e);
            return false;
        }
    }
    
    /**
     * 获取JVM启动时间
     * 
     * @return 启动时间戳
     */
    private static long getJVMStartTime() {
        try {
            return java.lang.management.ManagementFactory.getRuntimeMXBean().getStartTime();
        } catch (Exception e) {
            return System.currentTimeMillis() - 5000; // 默认5秒前
        }
    }
    
    /**
     * 执行高级校验
     * 
     * @param inputLicenseKey 输入的卡密
     * @return 校验结果
     */
    private static boolean performAdvancedValidation(String inputLicenseKey) {
        try {
            // 生成当前环境的唯一指纹
            String environmentFingerprint = generateEnvironmentFingerprint();
            
            // 使用环境指纹和卡密生成校验码
            String validationCode = generateSHA256(inputLicenseKey + ":" + environmentFingerprint + ":" + VALIDATION_SALT);
            
            // 检查校验码的某些特征（这里可以根据需要自定义规则）
            if (validationCode == null || validationCode.length() < 10) {
                return false;
            }
            
            // 检查校验码的特征（例如：必须包含某些字符模式）
            char[] chars = validationCode.toCharArray();
            int digitCount = 0;
            int letterCount = 0;
            
            for (char c : chars) {
                if (Character.isDigit(c)) digitCount++;
                if (Character.isLetter(c)) letterCount++;
            }
            
            // 校验码必须有合理的数字和字母比例
            return digitCount > 5 && letterCount > 5;
            
        } catch (Exception e) {
            logger.warn("高级校验异常", e);
            return false;
        }
    }
    
    /**
     * 生成环境指纹
     * 
     * @return 环境指纹字符串
     */
    private static String generateEnvironmentFingerprint() {
        StringBuilder fingerprint = new StringBuilder();
        
        // JVM信息
        fingerprint.append(System.getProperty("java.version", "unknown"));
        fingerprint.append("-");
        fingerprint.append(System.getProperty("os.name", "unknown"));
        fingerprint.append("-");
        fingerprint.append(System.getProperty("user.name", "unknown"));
        fingerprint.append("-");
        
        // 运行时信息
        fingerprint.append(Runtime.getRuntime().availableProcessors());
        fingerprint.append("-");
        fingerprint.append(System.getProperty("java.class.path", "").hashCode());
        
        return generateMD5(fingerprint.toString());
    }
    
    /**
     * 验证指定机器码和卡密的匹配关系
     * 
     * @param machineCode 机器码
     * @param licenseKey 卡密
     * @return true表示匹配，false表示不匹配
     */
    public static boolean validateLicenseForMachine(String machineCode, String licenseKey) {
        if (machineCode == null || licenseKey == null) {
            return false;
        }
        
        String expectedLicenseKey = generateLicenseKey(machineCode);
        return expectedLicenseKey != null && licenseKey.trim().equalsIgnoreCase(expectedLicenseKey.trim());
    }
} 